﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Interactivity;
using System.Windows;
using System.ComponentModel;
using System.Windows.Data;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Media;
using System.Collections;
using XWord.WPF.ApplicationFramework.Controls;

namespace XWord.WPF.Behaviors
{
    public class DelayBindingBehavior : Behavior<FrameworkElement>
    {
        protected override void OnAttached()
        {
            Initialize();

        }

        private MaskAttach maskAttach;
        private Binding binding;

        private void Initialize()
        {
            var bindableDpDec = DependencyPropertyDescriptor.FromName(BindableProperty, this.AssociatedObject.GetType(), this.GetType());
            _originProperty = bindableDpDec.DependencyProperty;
            BindingExpression expression = AssociatedObject.GetBindingExpression(_originProperty);
            binding = expression.ParentBinding;
            this.AssociatedObject.Loaded += BindingLoaded;
           
            BindingOperations.ClearBinding(AssociatedObject, _originProperty);
            if (AutoMask)
                ApplyMaskAttach();
        }



        private void BindingLoaded(object sender, RoutedEventArgs e)
        {
            switch (Time)
            {
                case BindingTime.Default:
                    Binding();
                    break;
                case BindingTime.InVisual:
                    if (VisualTreeHelper.GetParent(this.AssociatedObject) != null && !BindingOperations.IsDataBound(this, ValueProperty))
                    {
                        Binding();
                    }
                    break;
                case BindingTime.InVisualParent:
                    if (VisualTreeHelper.GetChildrenCount(this.AssociatedObject)>0 && !BindingOperations.IsDataBound(this, ValueProperty))
                    {
                        Binding();
                    }
                    break;
            }
        }

        private void BindingUnloaded(object sender, RoutedEventArgs e)
        {

            this.AssociatedObject.Loaded += BindingLoaded;
            this.AssociatedObject.Unloaded -= BindingUnloaded;
            BindingOperations.ClearBinding(this, ValueProperty);
        }

        private void Binding()
        {
            BindingOperations.SetBinding(this, ValueProperty, binding);
            this.AssociatedObject.Loaded -= BindingLoaded;
            switch (Mode)
            {
                case BindMode.Once:
                    break;
                case BindMode.ReBind:
                    this.AssociatedObject.Unloaded += BindingUnloaded;
                    break;
            }
        }

        

        private DataTemplate CreateDefaultTemplate()
        {
            DataTemplate tpl = new DataTemplate();
            FrameworkElementFactory borderFactory = new FrameworkElementFactory(typeof(Border));
            borderFactory.SetValue(Border.BackgroundProperty, Brushes.Silver);
            borderFactory.SetValue(Border.OpacityProperty, 0.7);
            FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBlock));
            textFactory.SetValue(TextBlock.VerticalAlignmentProperty, VerticalAlignment.Center);
            textFactory.SetValue(TextBlock.HorizontalAlignmentProperty, HorizontalAlignment.Center);
            textFactory.SetValue(TextBlock.ForegroundProperty, Brushes.SteelBlue);
            textFactory.SetValue(TextBlock.TextProperty, "加载中...");
            textFactory.SetValue(TextBlock.FontSizeProperty, 16.0);
            borderFactory.AppendChild(textFactory);
            tpl.VisualTree = borderFactory;
            return tpl;
        }

        private void ApplyMaskAttach()
        {
            if (maskAttach == null)
            {
                maskAttach = new MaskAttach();
                this.Dispatcher.BeginInvoke(new Action(() =>
                {
                    MaskAttach.SetContent(AssociatedObject, maskAttach);
                }), DispatcherPriority.Render);

            }
            if (LoadTemplate == null)
                maskAttach.Template = CreateDefaultTemplate();
            else
                maskAttach.Template = LoadTemplate;
        }

        public DataTemplate LoadTemplate
        {
            get { return (DataTemplate)GetValue(LoadTemplateProperty); }
            set { SetValue(LoadTemplateProperty, value); }
        }

        public static readonly DependencyProperty LoadTemplateProperty =
            DependencyProperty.Register("LoadTemplate", typeof(DataTemplate), typeof(DelayBindingBehavior),
            new UIPropertyMetadata(new PropertyChangedCallback((sender, args) =>
            {
                var behavior = sender as DelayBindingBehavior;
                if (args.NewValue != null && behavior.AutoMask)
                    behavior.ApplyMaskAttach();

            })));

        private DependencyProperty _originProperty;



        public bool AutoMask
        {
            get { return (bool)GetValue(AutoMaskProperty); }
            set { SetValue(AutoMaskProperty, value); }
        }

        
        public static readonly DependencyProperty AutoMaskProperty =
            DependencyProperty.Register("AutoMask", typeof(bool), typeof(DelayBindingBehavior), 
            new UIPropertyMetadata(true));



        public DispatcherPriority Priority
        {
            get { return (DispatcherPriority)GetValue(PriorityProperty); }
            set { SetValue(PriorityProperty, value); }
        }

        public static readonly DependencyProperty PriorityProperty =
            DependencyProperty.Register("Priority", typeof(DispatcherPriority), typeof(DelayBindingBehavior),
            new UIPropertyMetadata(DispatcherPriority.Background));

        public object Value
        {
            get { return (object)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register("Value", typeof(object), typeof(DelayBindingBehavior),
            new UIPropertyMetadata(new PropertyChangedCallback((sender, args) =>
            {
                var behavior = sender as DelayBindingBehavior;
                if (args.OldValue != null) behavior.oldValue = args.OldValue;
                if (args.NewValue == null || behavior.CheckList(behavior.oldValue, args.NewValue)) return;
                    behavior.ValueChanged(null, null);
            })));

        private object oldValue;

        //比较两个集合元素,相同则返回True
        private bool CheckList(object oldList, object newList)
        {
            var firstList = oldList as IList;
            var secondList = newList as IList;
            if (firstList == null || secondList == null) return false;
            if (firstList.Count != secondList.Count)
            {
                return false;
            }
  
            for (int i = 0; i < firstList.Count; i++)
            {
                if (!firstList[i].Equals(secondList[i]))
                {
                    return false;
                  
                }
            }
            return true;
        }

        public void ValueChanged(object sender, EventArgs e)
        {
            if(AutoMask) maskAttach.Open = true;
             this.Dispatcher.BeginInvoke(new Action(() =>
             {
                 AssociatedObject.SetValue(_originProperty, Value);
                 if (AutoMask) maskAttach.Open = false;
             }), DispatcherPriority.SystemIdle);
        }

        public string BindableProperty { get; set; }

        public BindingTime Time
        {
            get { return (BindingTime)GetValue(TimeProperty); }
            set { SetValue(TimeProperty, value); }
        }

        public static readonly DependencyProperty TimeProperty =
            DependencyProperty.Register("Time", typeof(BindingTime), typeof(DelayBindingBehavior), 
            new UIPropertyMetadata(BindingTime.Default));



        public BindMode Mode
        {
            get { return (BindMode)GetValue(ModeProperty); }
            set { SetValue(ModeProperty, value); }
        }

        public static readonly DependencyProperty ModeProperty =
            DependencyProperty.Register("Mode", typeof(BindMode), typeof(DelayBindingBehavior), 
            new UIPropertyMetadata(BindMode.Once));


    }

    public enum BindingTime
    {
        Default,
        InVisual,
        InVisualParent
    }

    public enum BindMode
    {
        /// <summary>
        /// 绑定一次,若绑定数据不变,只是延迟则选此项
        /// </summary>
        Once,
        /// <summary>
        /// 在Unload时候去除绑定,Loaded时重新绑定,若为列表Detail绑定,则建议选此项
        /// </summary>
        ReBind
    }
}